// TODO(lilli): draw obstacle boxes with mouse drags

EditableLaser.prototype = new Mirror();
function EditableLaser(imageFile) {	
	this.rot = 0;
	this.imageFile = imageFile;
	this.image = null;

	EditableLaser.prototype.render = function(processing) {
		// load the image file if it hasn't been loaded already
		if (this.image == null) {
			this.image = processing.loadImage(this.imageFile);
		}
		
		processing.pushMatrix();
			
		// translate to the screen position.
		processing.translate(this.px, this.py);
		
		// Draw the mirror's bounding box, which has no rotation information.
		processing.noFill();
		
		// Change the bounding box color if the mirror is selected.
		if (this.isSelected) {
			processing.stroke(0, 255, 0);
		} else if(this.isPlaced) {
			processing.noStroke();
		} else {
			processing.stroke(0, 0, 0);
		}
		
		processing.quad(
			-1 * this.halfLength, this.halfLength,
			this.halfLength, this.halfLength,
			this.halfLength, -1 * this.halfLength,
			-1 * this.halfLength, -1 * this.halfLength
		);
			
		// Draw the mirror itself, which is rotated.
		processing.rotate(processing.radians(this.rot));
		
		// Draw the mirror quad.			
		processing.image(this.image, -1 * this.halfLength, -1 * this.halfLength); 
		processing.popMatrix();	
	}	

	EditableLaser.prototype.newLaserAngle = function(vectorX, vectorY, vectorAngle) {
		return this.rot;
	}
}

EditableTarget.prototype = new Mirror();
function EditableTarget() {	
	this.rot = 0;

	EditableTarget.prototype.render = function(processing) {	
		processing.pushMatrix();
			
		// translate to the screen position.
		processing.translate(this.px, this.py);
		
		// Draw the mirror's bounding box, which has no rotation information.
		processing.noFill();
		
		// Change the bounding box color if the mirror is selected.
		if (this.isSelected) {
			processing.stroke(0, 255, 0);
		} else if(this.isPlaced) {
			processing.noStroke();
		} else {
			processing.stroke(0, 0, 0);
		}
		processing.quad(
			-1 * this.halfLength, this.halfLength,
			this.halfLength, this.halfLength,
			this.halfLength, -1 * this.halfLength,
			-1 * this.halfLength, -1 * this.halfLength
		);
			
		processing.rotate(processing.radians(this.rot));
		processing.fill(255, 0, 0);		
		processing.ellipse(0, 0, this.halfLength, this.halfLength);
		processing.popMatrix();	
	}	

	EditableTarget.prototype.newLaserAngle = function(vectorX, vectorY, vectorAngle) {
		return this.rot;
	}
}

// An editor is a special kind of level with custom behavior
// for user input processing.
function Editor(processing) {	
	// Currently selected thing. Can be null if nothing is selected.
	this.selected = null;
	
	// The static and dynamic objects are copied into variables on this
	// class to avoid overly long calls to them.
	this.dynamicObjects = new Array();	
	this.staticObjects = new Array();	
	this.allObjects = new Array();	
	this.winConditions = new Array();
	
	this.r = 0;
	this.g = 0;
	this.b = 0;
		
	// The editor is a level, but it is not winnable.
	this.levelCleared = false;

	// Make an instance of every object that can be placed in the level.
	this.dynamicObjects[0] = new Mirror(0);
	this.dynamicObjects[1] = new Lens(0, "rightArrow.gif");
 	this.dynamicObjects[2] = new RightRotator("rightRotate.gif");
 	this.dynamicObjects[3] = new LeftRotator("leftRotate.gif");
 	this.dynamicObjects[4] = new Reverser("reverser.gif");
	this.dynamicObjects[5] = new EditableLaser("laser.gif");
	this.dynamicObjects[6] = new EditableTarget();

	Editor.prototype.render = function(processing) {
	 	// Reset all the win conditions, because they need
	 	// to be rechecked each frame.
	 	for (var i=0; i<this.winConditions.length; ++i) {
 			this.winConditions[i].reset();
 		}
	 		
 		// Clear the background by rendering a transparent quad over the
 		// background image.
 		for (var x = 0; x < $x; x += 75) {
 			for (var y = 0; y < $y; y += 75) {
 				processing.image($editorBackground, x, y);	
 			}
 		}
 				
 		// Render all the static objects.
		for (var i=0; i<this.staticObjects.length; i++) {
		  this.staticObjects[i].update(this.allObjects);
		  this.staticObjects[i].render(processing);
 		}
 		for (var i=0; i< this.winConditions.length; i++) {
		  this.winConditions[i].render(processing);
 		}
 		
 		// Render the available objects area.
 		processing.fill(this.r, this.g, this.b);
 		processing.quad(
 			0, $y,
 			$x, $y,
 			$x, $y + $availableHeight,
 			0, $y + $availableHeight);
 		
 		// Render all the dynamic objects.
 		var nextAvailableObjectXCoordinate = 0;		
 		for (var i=0; i<this.dynamicObjects.length; i++) {	
 			if (this.dynamicObjects[i].isPlaced) {
 			
 				// Render all objects placed in the world
 				this.dynamicObjects[i].render(processing);
 			} else {
 			
 				// For objects yet to be placed, force them to exist in the
 				// available objects area.
 				this.dynamicObjects[i].setPosition(
 					nextAvailableObjectXCoordinate + this.dynamicObjects[i].halfLength + $availableSpacing,
 					$y + this.dynamicObjects[i].halfLength + $availableSpacing);
 			
 				this.dynamicObjects[i].render(processing);
 				nextAvailableObjectXCoordinate += (this.dynamicObjects[i].halfLength * 2) + $availableSpacing;
 			}					
 		}
 		
 		// Now that everything has been udpated, check to see if this level
 		// has been won.
 		this.checkLevelOver();
 		
 		// If the level has been won, fade out the background.
 		if (this.winPending) {
 			this.fadeBackground();
 			processing.fill(150, 255, 0, this.clearAlpha);
 			processing.pushMatrix();
 			processing.scale(8, 8);
 			processing.text("Stage Clear!", 15, 20);
 			processing.popMatrix();
 		}
	}
		
	Editor.prototype.checkLevelOver = function() {
 		// Editor levels are not winnable. They go on forever.
	}
	
	// --- Mouse Handlers ---
	
	Editor.prototype.mousePressed = function(mouseX, mouseY) {	
		
                // If win is pending, mouse click finalizes it.
	        if (this.winPending) {
		  this.levelCleared = true;
		  return;
		}
		// TODO(lilli): remove this code once we have a better way to win levels.
		// Send the mouse click to the win conditions. For now this is necessary
 		// because the win condition for targets is that they are clicked, but
 		// that's kind of silly. We can probably remove this once the laser can
 		// interact with targets nicely.
 		for (var i=0; i<this.winConditions.length; ++i) {
 			this.winConditions[i].processMouse(mouseX, mouseY);
 		}
 		// We want this check in the update loop instead. TODO(lilli): move it.
		this.checkLevelOver();
	        if (this.winPending) {
		  this.levelCleared = true;
		  return;
		}
		
		// First check to see if the user is placing a new item from the
		// available objects list.
		for (var i=0; i<this.dynamicObjects.length; i++) {
 			if (this.dynamicObjects[i].collides(mouseX, mouseY)) {
 				
 				// Unselect the currently selected thing
 				if (this.selected != null) {
 					this.selected.isSelected = false;
 				}
 			
 				// Select the new thing.
 				this.dynamicObjects[i].isSelected = true;
 				this.selected = this.dynamicObjects[i];
 				
 				// Clone the object if it is not placed yet
 				if (!this.dynamicObjects[i].isPlaced) {
 					var clone = jQuery.extend(true, {}, this.dynamicObjects[i]);
 					this.dynamicObjects[this.dynamicObjects.length] = clone;
 					clone.isSelected = false;
 				}
 				
 				// Move this object from the available list into the
 				// placed list.
 				this.dynamicObjects[i].isPlaced = true;
 							
 				return;
 			}
 		}	
 			 		
 		// If this mouse click didn't collide with any object, then
 		// unselect the currently selected thing.
 		if (this.selected != null) {
 			this.deselectAll();
 		}	
	}
	
	Editor.prototype.mouseReleased = function() {
		// If the user had an object selected, figure out
		// whether the object was dropped on the main canvas or
		// the available object area and snap the object to one
		// or the other.
		
		if (this.selected != null) {
		
			// Constrain first off the top and sides
			if (this.selected.px < this.selected.halfLength) {
				this.selected.px = this.selected.halfLength;
			}
			if (this.selected.px > $x - this.selected.halfLength) {
				this.selected.px = $x - this.selected.halfLength;
			}
			if (this.selected.py < this.selected.halfLength) {
				this.selected.py = this.selected.halfLength;
			}
			
			// If the object is more than half in the play area, snap it there.
			// Otherwise return it to its unplaced state.
			if (this.selected.py > $y) {
				this.selected.isPlaced = false;
			} else if (this.selected.py > $y - this.selected.halfLength) {
				this.selected.py = $y - this.selected.halfLength;
			}	
		}
	}
	
	Editor.prototype.mouseDragged = function(mouseX, mouseY) {
		if (this.selected == null) {
 			return;
 		}
 		this.selected.setPosition(mouseX, mouseY);
	}
	
	Editor.prototype.keyPressed = function(keyCode) {
	
		// 'r' rotates the current thing by 5 degrees.
		if (keyCode == 114) {
			if (this.selected != null) {
				this.selected.addRotation(5);
			}
		} 
		
	}
}